package com.example.websocket.another.config;

import com.example.websocket.another.component.WebSocketSendAsync;
import com.example.websocket.another.constant.RedisKeyPrefix;
import com.example.websocket.another.entity.UserEntity;
import com.example.websocket.another.service.UserMessageService;
import com.example.websocket.another.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

import javax.annotation.PostConstruct;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Created by rajeevkumarsingh on 25/07/17.
 */
@Component
@Slf4j
public class WebSocketEventListener {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private WebSocketSendAsync webSocketSendAsync;
    @Autowired
    private UserMessageService userMessageService;


    @PostConstruct
    public void init() {
        Set<String> keys = stringRedisTemplate.keys(RedisKeyPrefix.WEBSOCKET_REDIS_KEY + "*");
        if (!CollectionUtils.isEmpty(keys)) {
            stringRedisTemplate.delete(keys);
            log.info("删除所有登陆的key:" + keys);
        }
    }

    @EventListener
    public void handleWebSocketConnectListener(SessionConnectedEvent event) throws Exception {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());

        log.info("创建新连接----》");
        // token获取id
        Long id = getUserIdByToken(accessor);
        if (id == null) {
            return;
        }
        String sessionId = accessor.getSessionId();
        //当前用户登陆前检查 之前是否已经连接,抛异常无法断开本次连接
//        String sessionConnected = stringRedisTemplate.opsForValue().get("websocket:" + id);
//        if (sessionConnected != null) {
//            throw new Exception("You had a connected for this account!");
//        }

        stringRedisTemplate.opsForValue().set(RedisKeyPrefix.WEBSOCKET_REDIS_KEY + id, sessionId);
        log.info("用户唯一标识：{}，session 标识：{}", id, sessionId);
        //发送在线用户列表
        webSocketSendAsync.sendToAllUsers();
        //发送离线未接收到的消息
        userMessageService.sendToThisConnectUserMessage(id, sessionId);
    }

    @EventListener
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        Long id = (Long) Objects.requireNonNull(headerAccessor.getSessionAttributes()).get("userId");
        if (id == null) {
            return;
        }
        log.info("断开连接 User Disconnected : " + id);
        stringRedisTemplate.delete(RedisKeyPrefix.WEBSOCKET_REDIS_KEY + id);
        webSocketSendAsync.sendToAllUsers();
    }


    /**
     * 根据token获取id
     *
     * @param accessor
     * @return
     */
    public Long getUserIdByToken(StompHeaderAccessor accessor) throws Exception {
        GenericMessage messageHeaderAccessor = (GenericMessage) accessor.getHeader("simpConnectMessage");
        assert messageHeaderAccessor != null;
        Map<String, Object> nativeHeaders = (Map<String, Object>) messageHeaderAccessor.getHeaders().get("nativeHeaders");
        LinkedList tokens = (LinkedList) nativeHeaders.get(JwtUtil.header);
        String token = (String) tokens.getFirst();
        if (token == null) {
            log.info("未获取用户唯一标识 token！");
            return null;
        }
        UserEntity entity = JwtUtil.validateTokenGetUser(token);
        if (entity == null || entity.getId() == null) {
            log.info("未获取用户唯一标识 id！");
            return null;
        }
        // id放入会话中
        Map<String, Object> simpSessionAttributes = (Map<String, Object>) messageHeaderAccessor.getHeaders().get("simpSessionAttributes");
        simpSessionAttributes.put("userId", entity.getId());
        return entity.getId();
    }

}
